package org.adream.dreamnt;

import org.jasig.cas.ticket.ServiceTicket;
import org.jasig.cas.ticket.Ticket;
import org.jasig.cas.ticket.TicketGrantingTicket;
import org.jasig.cas.ticket.registry.AbstractDistributedTicketRegistry;
import org.jasig.cas.ticket.registry.TicketRegistry;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.data.redis.core.RedisTemplate;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

public class RedisTicketRegistry extends AbstractDistributedTicketRegistry implements DisposableBean, TicketRegistry {

	Logger logger = LoggerFactory.getLogger(this.getClass());

	/** redis client. */
	@NotNull
	private final TicketRedisTemplate redisTemplate;
	/**
	 * TGT cache entry timeout in seconds.
	 */
	@Min(0)
	private final int tgtTimeout;
	/**
	 * ST cache entry timeout in seconds.
	 */
	@Min(0)
	private final int stTimeout;
	private final String PREFIX_CAS = "ACCOUNT.ADREAM.ORG:CAS:TICKET:";

	public RedisTicketRegistry(TicketRedisTemplate redisTemplate, int tgtTimeout, int stTimeout) {
		this.redisTemplate = redisTemplate;
		this.tgtTimeout = tgtTimeout;
		this.stTimeout = stTimeout;
	}

	@Override
	public void addTicket(Ticket ticket) {
		logger.info("Add ticket {}", ticket);
		try {
			this.redisTemplate.boundValueOps(PREFIX_CAS + ticket.getId()).set(ticket, getTimeout(ticket),
					TimeUnit.SECONDS);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public Ticket getTicket(String ticketId) {
		logger.info("Get ticket {}", ticketId);
		try {
			Ticket t = (Ticket) this.redisTemplate.boundValueOps(PREFIX_CAS + ticketId).get();
			if (t != null) {
				return getProxiedTicketInstance(t);
			}
		} catch (final Exception e) {
			logger.error("Failed fetching {} ", ticketId, e);
		}
		return null;
	}

	@Override
	public Collection<Ticket> getTickets() {
		Set<Ticket> tickets = new HashSet<Ticket>();
		Set<String> keys = this.redisTemplate.keys(PREFIX_CAS + "*");
		for (String key : keys) {
			Ticket ticket = (Ticket) this.redisTemplate.boundValueOps(key).get();
			if (ticket == null)
				this.redisTemplate.delete(key);
			else {
				tickets.add(ticket);
			}
		}
		return tickets;
	}

	@Override
	protected boolean needsCallback() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	protected void updateTicket(Ticket ticket) {
		logger.info("Updating ticket {}", ticket);
		try {
			this.redisTemplate.boundValueOps(PREFIX_CAS + ticket.getId()).set(ticket, getTimeout(ticket),
					TimeUnit.SECONDS);
		} catch (final Exception e) {
			logger.error("Failed updating {}", ticket, e);
		}
	}

	@Override
	public boolean deleteSingleTicket(String ticketId) {
		logger.debug("Deleting Single Ticket {}", ticketId);
		try {

			this.redisTemplate.delete(PREFIX_CAS + ticketId);
			return true;
		} catch (final Exception e) {
			logger.error("Failed deleting {}", ticketId, e);
		}
		return false;
	}

	@Override
	public void destroy() throws Exception {
		// TODO Auto-generated method stub
	}

	/**
	 * Gets the timeout value for the ticket.
	 *
	 * @param t
	 *            the t
	 * @return the timeout
	 */
	private int getTimeout(final Ticket t) {
		if (t instanceof TicketGrantingTicket) {
			return this.tgtTimeout;
		} else if (t instanceof ServiceTicket) {
			return this.stTimeout;
		}
		throw new IllegalArgumentException("Invalid ticket type");
	}

}
